package code.google.jcustomize;

import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.FunctionMapper;
import javax.servlet.jsp.el.VariableResolver;

import org.apache.commons.el.ExpressionEvaluatorImpl;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

abstract class AbstractConfig {
	public final static String DEFAULT_CONFIG_TYPE		= "xml";
	public final static String CONFIG_CLASS_PREFIX		= "";
	public final static String CONFIG_CLASS_SUFFIX		= "Config";
	
    private String name; 
	private String skipCondition;
	
	protected abstract void readItems(Node config);
	
	public abstract boolean patch(ConfigSource cs, VariableResolver variableResolver, FunctionMapper functionMapper);
	
	public boolean skip(VariableResolver variableResolver, FunctionMapper functionMapper) {
		if (skipCondition == null || "".equals(skipCondition)) return false;
		ExpressionEvaluator ee = new ExpressionEvaluatorImpl();
		try {
			Boolean result = (Boolean)ee.evaluate(skipCondition, java.lang.Boolean.class, variableResolver, functionMapper);
			return result != null ? result.booleanValue() : false;
		} catch (ELException e) {
			e.printStackTrace();
			return true;
		}
	}
	
	public static AbstractConfig read(Node config) throws ClassNotFoundException {
		if (config == null || config.getNodeType() != Node.ELEMENT_NODE)
			throw new IllegalArgumentException("Invalid config entry: " + (config == null ? "null" : ("node type is "+config.getNodeType())));
		Element cfg = (Element)config;
		String configType = cfg.getAttribute("type");
		if ("".equals(configType)) configType = DEFAULT_CONFIG_TYPE;
		
		String configClassPackage = AbstractConfig.class.getPackage().getName();
		String configClassName = CONFIG_CLASS_PREFIX + Character.toUpperCase(configType.charAt(0)) + configType.substring(1) + CONFIG_CLASS_SUFFIX;
		
		Class configClass = Class.forName(configClassPackage + "." + configClassName);
		try {
			AbstractConfig configImpl = (AbstractConfig)configClass.newInstance();
			configImpl.setSkipCondition(cfg.getAttribute("skip-condition"));
            configImpl.setName(cfg.getAttribute("name"));
			configImpl.readItems(config);
			return configImpl;
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String getElementValue(Node node) {
		StringBuffer sb = new StringBuffer();
		for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
			if (child.getNodeType() != Node.TEXT_NODE) continue;
			String value = child.getNodeValue();
			if (value != null) sb.append(value);
		}
		return sb.toString();
	}

	private String getSkipCondition() {
		return skipCondition;
	}

	private void setSkipCondition(String skipCondition) {
		this.skipCondition = skipCondition;
	}
    
    public String getName() {
        return name;
    }
    
    protected void setName(String name) {
        this.name = name;
    }
	
}
